﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PagedListView.cs" company="XamlNinja">
//   2011 Richard Griffin and Ollie Riches
// </copyright>
// <summary>
//   Generic bindable view for paged lists, this supports binding to controls via the IEnumerable interface, the IPagedCollectionView
//   supplies all the methods required for a DataPager control. The _movePageFunc private property is the intelligent part of the class,
//   the class its self doesn't know how a move to another page is performed.
//   The operation of moving to another page has a boolean result - either successful or failure.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace WP7Contrib.Collections
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Linq;
    using System.Reactive.Linq;
    using Common;

    /// <summary>
    /// Generic bindable view for paged lists, this supports binding to controls via the IEnumerable interface, the IPagedCollectionView
    /// supplies all the methods required for a DataPager control. The _movePageFunc private property is the intelligent part of the class,
    /// the class its self doesn't know how a move to another page is performed.
    /// The operation of moving to another page has a boolean result - either successful or failure.
    /// </summary>
    /// <typeparam name="T">
    /// Where T represents the type instance you want to bind to a control.
    /// </typeparam>
    public sealed class PagedListView<T> : BaseCollectionModel, IEnumerable, IPagedCollectionView<T>
        where T : class, new()
    {
        /// <summary>
        /// The move page func - the PageListView does not know how to move a page, only how to initiate the call.
        /// </summary>
        private Func<int, int, IObservable<PagedList<T>>> movePageFunc;

        /// <summary>
        /// The paged list.
        /// </summary>
        private IPagedList<T> pagedList;

        /// <summary>
        /// Initializes a new instance of the <see cref="PagedListView{T}"/> class.
        /// </summary>
        /// <param name="movePageFunc">
        /// The move page func.
        /// </param>
        public PagedListView(Func<int, int, IObservable<PagedList<T>>> movePageFunc)
            : this(0, movePageFunc)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PagedListView{T}"/> class.
        /// </summary>
        /// <param name="pageSize">
        /// The page size.
        /// </param>
        /// <param name="movePageFunc">
        /// The move page func.
        /// </param>
        public PagedListView(int pageSize, Func<int, int, IObservable<PagedList<T>>> movePageFunc)
            : this(pageSize, 0, 0, new List<T>(), movePageFunc)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PagedListView{T}"/> class.
        /// </summary>
        /// <param name="pageSize">The page size.</param>
        /// <param name="pageIndex">The current page index.</param>
        /// <param name="source">The source collection to initialise with.</param>
        /// <param name="movePageFunc">The move page func.</param>
        
        /// <param name="totalCount"></param>
        public PagedListView(int pageSize, int pageIndex, int totalCount, IEnumerable<T> source, Func<int, int, IObservable<PagedList<T>>> movePageFunc)
        {
            this.pagedList = new PagedList<T>(pageSize)
                                 {
                                    Items = source.ToList(),
                                    PageIndex = pageIndex,
                                    Total = totalCount
                                 };

            this.movePageFunc = movePageFunc;
        }

        #region IEnumerable Members

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// Returns an enumerator for the collection.
        /// </returns>
        public IEnumerator GetEnumerator()
        {
            return this.pagedList.Items.GetEnumerator();
        }

        #endregion

        #region IPagedCollectionView<T> Members

        /// <summary>
        /// Gets the item count for the collection.
        /// </summary>
        public int ItemCount
        {
            get { return this.pagedList.Items.Count; }
        }

        /// <summary>
        /// Gets or sets the move next function.
        /// </summary>
        public Func<int, int, IObservable<PagedList<T>>> MovePageFunc
        {
            get
            {
                return this.movePageFunc;
            }
            set
            {
                this.movePageFunc = value;
            }
        }

        /// <summary>
        /// Gets or sets the page index of the collection.
        /// </summary>
        public int PageIndex
        {
            get
            {
                return this.pagedList.PageIndex;
            }
            set
            {
                this.pagedList.PageIndex = value;
                this.RaisePropertyChanged(() => this.PageIndex);
            }
        }

        /// <summary>
        /// Gets or sets the page size of the collection.
        /// </summary>
        public int PageSize
        {
            get
            {
                return this.pagedList.PageSize;
            }
            set
            {
                this.pagedList.PageSize = value;
                this.RaisePropertyChanged(() => this.PageSize);
            }
        }

        /// <summary>
        /// Gets or sets the source collection for the view.
        /// </summary>
        public IPagedList<T> Source
        {
            get
            {
                return this.pagedList;
            }
            private set
            {
                this.pagedList = value;

                this.RaiseCollectionChanged(NotifyCollectionChangedAction.Reset);
                this.RaisePropertyChanged(() => this.Source);
            }
        }

        /// <summary>
        /// Gets the total item count foe the collection.
        /// </summary>
        public int TotalItemCount
        {
            get
            {
                return this.pagedList.Total;
            }
        }

        /// <summary>
        /// Attempts to move to first page.
        /// </summary>
        /// <returns>
        /// Returns true if moving to the first page was completed.
        /// </returns>
        public bool MoveToFirstPage()
        {
            return this.MoveToPage(0, this.PageSize);
        }

        /// <summary>
        /// Attempts to move to last page.
        /// </summary>
        /// <returns>
        /// Returns true if moving to the last page was completed.
        /// </returns>
        public bool MoveToLastPage()
        {
            if (this.pagedList.PageSize < 1)
            {
                return false;
            }

            return this.MoveToPage(this.TotalNumberOfPages(), this.PageSize);
        }

        /// <summary>
        /// Attempts to move to next page.
        /// </summary>
        /// <returns>
        /// Returns true if moving to the next page was completed.
        /// </returns>
        public bool MoveToNextPage()
        {
            return this.MoveToPage(this.pagedList.PageIndex + 1, this.PageSize);
        }

        /// <summary>
        /// Returns true if the next page can moved to.
        /// </summary>
        /// <returns>Returns true if the next page can moved to.</returns>
        public bool IsThereNextPage()
        {
            var pageIndex = this.pagedList.PageIndex + 1;
            return this.CanMoveToPage(pageIndex, this.PageSize);
        }
        
        /// <summary>
        /// Attempts to move to previous page.
        /// </summary>
        /// <returns>
        /// Returns true if moving to the previous page was completed.
        /// </returns>
        public bool MoveToPreviousPage()
        {
            return this.MoveToPage(this.pagedList.PageIndex - 1, this.PageSize);
        }

        /// <summary>
        /// Returns true if the previous page can moved to.
        /// </summary>
        /// <returns>Returns true if the previous page can moved to.</returns>
        public bool IsTherePreviousPage()
        {
            var pageIndex = this.pagedList.PageIndex - 1;
            return this.CanMoveToPage(pageIndex, this.PageSize);
        }

        /// <summary>
        /// Attempts to move to a particular page.
        /// </summary>
        /// <param name="pageIndex">
        /// The page index attempting to move to.
        /// </param>
        /// <returns>
        /// Returns true if the moving to the particular page was completed.
        /// </returns>
        public bool MoveToPage(int pageIndex)
        {
            return this.MoveToPage(pageIndex, this.PageSize);
        }

        /// <summary>
        /// Determines if a page can be moved to.
        /// </summary>
        /// <param name="pageIndex">The page index</param>
        /// <param name="pageSize">The page size</param>
        /// <returns>Returns true if we can move to the page</returns>
        public bool CanMoveToPage(int pageIndex, int pageSize)
        {
            if (pageIndex < 0)
            {
                return false;
            }

            if (pageSize < 1)
            {
                return false;
            }

            if (this.pagedList.Total == 0)
            {
                return false;
            }

            var totalNumberofPages = this.pagedList.Total / pageSize;
            if (this.pagedList.Total % pageSize != 0)
            {
                totalNumberofPages++;
            }

            if (pageIndex > totalNumberofPages)
            {
                return false;
            }

            return true;
        }

        #endregion

        /// <summary>
        /// Calculates the total number of pages available.
        /// </summary>
        /// <returns>
        /// Returns the total number of pages.
        /// </returns>
        public int TotalNumberOfPages()
        {
            var totalNumberofPages = this.pagedList.Total / this.pagedList.PageSize;
            if (this.pagedList.Total % this.pagedList.PageSize != 0)
            {
                totalNumberofPages++;
            }

            return totalNumberofPages;
        }

        /// <summary>
        /// Attempts to move to the page.
        /// </summary>
        /// <param name="pageIndex">The index of the page to move to.</param>
        /// <param name="pageSize">The size of the page to be returned.</param>
        /// <returns>Returns true if the move page was successful.</returns>
        private bool MoveToPage(int pageIndex, int pageSize)
        {
            try
            {
                if (this.movePageFunc == null)
                {
                    return false;
                }

                this.movePageFunc(pageIndex, pageSize)
                    .ObserveOnDispatcher()
                    .Subscribe(p => this.Source = p);
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }
    }
}